﻿using System;
using System.Collections.Generic;
using System.Text;

namespace HzySocket.WebSocket.Core
{
    using Microsoft.AspNetCore.Http;
    using System.Threading.Tasks;
    using HzySocket.WebSocket.Interface;
    using System.Threading;
    using System.Linq;
    using System.Net.WebSockets;
    using System.IO;

    public abstract class HzyWebSocketMiddlewareBase<TSession> : IMiddleware, IWebSocketMiddlewareBase<TSession>
        where TSession : WebSocketSessionBase, new()
    {
        /// <summary>
        /// 不活跃秒数 将被清除连接对象
        /// </summary>
        protected static int ActivityTime { get; set; } = 15;
        /// <summary>
        /// 非活跃 服务 Session 清除事件 服务会话主动断开清除事件 传入参数 TAppSession
        /// </summary>
        protected static Action<TSession> InactiveCloseCallBack { get; set; } = null;
        /// <summary>
        /// 连接关闭事件 传入参数 TAppSession
        /// </summary>
        protected static Action<TSession> ConnectCloseCallBack { get; set; } = null;
        /// <summary>
        /// 发送异常事件回调
        /// </summary>
        protected static Action<Exception> SendExceptionCall { get; set; } = null;
        /// <summary>
        /// 新的session连接
        /// </summary>
        protected static Action<TSession> ConnectCall { get; set; } = null;
        /// <summary>
        /// 更新AppSession回调
        /// </summary>
        protected static Action<TSession> UpdateAppSessionCall { get; set; } = null;

        /// <summary>
        /// 消息接收函数
        /// </summary>
        /// <param name="appSession"></param>
        /// <returns></returns>
        public abstract Task ExecuteCommand(TSession appSession);

        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            //检查是否是 WebSocket 类型请求
            if (!context.WebSockets.IsWebSocketRequest)
            {
                await next.Invoke(context);
                return;
            }

            //链接 地址 后面 必须跟 /ws 路由 或者 /wss
            if (context.Request.Scheme == "https" && !context.Request.Path.ToString().StartsWith("/wss"))
            {
                await next.Invoke(context);
                return;
            }
            if (context.Request.Scheme == "http" && !context.Request.Path.ToString().StartsWith("/ws"))
            {
                await next.Invoke(context);
                return;
            }

            var _AcceptWebSocketAsync = await context.WebSockets.AcceptWebSocketAsync();

            CancellationToken _CancellationToken = context.RequestAborted;
            //检查服务端 WebSocketSession 是否存在
            var tappSession = WebSocketSessionContainer<TSession>.GetAppSessions(w => w.SessionKey == context.Connection.Id).FirstOrDefault();
            if (tappSession == null)
            {
                tappSession = new TSession();
                tappSession.StartTime = DateTime.Now;
                tappSession.SessionKey = context.Connection.Id;
                tappSession.SendExceptionCall = SendExceptionCall;
                //
                tappSession.WebSocket = _AcceptWebSocketAsync;
                tappSession.HttpContext = context;
                tappSession.GetCancellationToken = _CancellationToken;
                tappSession.LastActiveTime = DateTime.Now;
                await tappSession.SendAsync(new { status = 1, msg = "WebSocket 连接已建立,请开始注册用户!" });
                if (ConnectCall != null) ConnectCall.Invoke(tappSession);
            }
            else
            {
                tappSession.WebSocket = _AcceptWebSocketAsync;
                tappSession.HttpContext = context;
                tappSession.GetCancellationToken = _CancellationToken;
                tappSession.LastActiveTime = DateTime.Now;
                if (UpdateAppSessionCall != null) UpdateAppSessionCall.Invoke(tappSession);
            }

            //var buffer = new byte[1024 * 20];
            //WebSocketReceiveResult result = await _AcceptWebSocketAsync.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            //检查和处理客户端发送的数据
            while (!_AcceptWebSocketAsync.CloseStatus.HasValue)
            {
                if (_CancellationToken.IsCancellationRequested) break;
                string data = await ReceiveStringAsync(_AcceptWebSocketAsync, _CancellationToken);
                if (string.IsNullOrWhiteSpace(data)) continue;
                tappSession.Data = data;
                //执行工作函数
                await this.ExecuteCommand(tappSession);
                WebSocketSessionContainer<TSession>.AddOrUpdateAppSession(tappSession);
            }

            //连接断开后移除 session 对象
            if (ConnectCloseCallBack != null) { ConnectCloseCallBack?.Invoke(tappSession); }
            WebSocketSessionContainer<TSession>.RemoveSession(w => w.SessionKey == context.Connection.Id);

            //await _AcceptWebSocketAsync.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
            await _AcceptWebSocketAsync.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", _CancellationToken);
            _AcceptWebSocketAsync.Dispose();
            //.....
            //参考了：https://www.cnblogs.com/besuccess/p/7043885.html
        }

        private static async Task<string> ReceiveStringAsync(WebSocket socket, CancellationToken ct = default(CancellationToken))
        {
            var buffer = new ArraySegment<byte>(new byte[1024 * 5]);

            using (var ms = new MemoryStream())
            {
                WebSocketReceiveResult _WebSocketReceiveResult;
                do
                {
                    ct.ThrowIfCancellationRequested();
                    _WebSocketReceiveResult = await socket.ReceiveAsync(buffer, ct);
                    ms.Write(buffer.Array, buffer.Offset, _WebSocketReceiveResult.Count);
                }
                while (!_WebSocketReceiveResult.EndOfMessage);

                ms.Seek(0, SeekOrigin.Begin);
                if (_WebSocketReceiveResult.MessageType != WebSocketMessageType.Text) return null;

                using (var reader = new StreamReader(ms, Encoding.UTF8))
                {
                    return await reader.ReadToEndAsync();
                }
            }
        }

        /// <summary>
        /// 检查不活跃Session信息
        /// </summary>
        /// <returns></returns>
        protected static Task CheckInactiveSessionAsync()
        {
            //移除不活跃的SocketSession对象
            var _TAppSessionList = WebSocketSessionContainer<TSession>.GetAppSessions(w => (DateTime.Now - w.LastActiveTime).TotalSeconds >= ActivityTime);
            if (_TAppSessionList?.Count() > 0)
            {
                try
                {
                    foreach (var item in _TAppSessionList)
                    {
                        if (item.WebSocket.State == WebSocketState.Open)
                        {
                            InactiveCloseCallBack?.Invoke(item);
                            //await item.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", item.GetCancellationToken);
                        }
                    }
                }
                catch (Exception) { }

                WebSocketSessionContainer<TSession>.RemoveSession(w => (DateTime.Now - w.LastActiveTime).TotalSeconds >= ActivityTime);
            }

            return Task.CompletedTask;
        }




    }
}
